package com.orange.delivery.presentation.page;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.OrderByBorder;
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.navigation.paging.IPageable;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.request.resource.PackageResourceReference;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;

import com.francetelecom.clara.component.wicket.app.buttons.OButtonStrong;
import com.francetelecom.clara.component.wicket.app.page.ODynamicFeedbackPanel;
import com.francetelecom.clara.component.wicket.app.paginate.OAjaxPagingNavigator;
import com.francetelecom.clara.component.wicket.app.popin.OPopinAjaxOpenLink;
import com.francetelecom.clara.component.wicket.app.popin.OPopinContainer;
import com.orange.delivery.application.ManageMarket;
import com.orange.delivery.model.MarketBean;
import com.orange.delivery.presentation.common.HeaderFooterTemplatePage;
import com.orange.delivery.presentation.event.CreateOrUpdateMarketEvent;
import com.orange.delivery.presentation.model.SearchParameterBean;
import com.orange.delivery.presentation.panel.CreateMarketPanel;
import com.orange.delivery.presentation.panel.UpdateMarketPanel;
import com.orange.delivery.presentation.util.CounterModel;
import com.orange.delivery.technicalservice.exception.InvalidMarketException;
import com.orange.delivery.technicalservice.exception.NotFoundException;
import com.orange.delivery.technicalservice.exception.TechnicalException;

@AuthorizeInstantiation("ROLE_ADMIN")
public class MarketPage extends HeaderFooterTemplatePage {

	private static final long serialVersionUID = 1L;

	private static final Logger LOG = LoggerFactory.getLogger(MarketPage.class.getName());

	@SpringBean(name = "marketLinesPerPage")
	private Integer LINES_PER_PAGE;

	private int currentPage = 0;

	/**
	 * application layer manager
	 */
	@SpringBean(name = "manageMarketApplication")
	private ManageMarket manageMarket;

	private long size;

	private Sort sort;

	private IModel<Long> sizeModel = Model.of(size);

	boolean formSubmitted;
	ODynamicFeedbackPanel feedbackConfirm;

	/**
	 * MarketPage constructor
	 */
	public MarketPage() {
		super();
		LOG.debug("create market page");
		createMarketPageTitle();
		createBodyComponents();
	}

	private void createMarketPageTitle() {
		Label marketPageTitle = new Label("marketPageTitle", getString("market.page.title"));
		add(marketPageTitle);
	}

	private void createBodyComponents() {

		final WebMarkupContainer dataContainer = new WebMarkupContainer("dataContainer");
		dataContainer.setOutputMarkupId(true);
		add(dataContainer);

		// =====================================================
		// panel for warning messages
		// =====================================================

		// feedback information

		feedbackConfirm = new ODynamicFeedbackPanel("feedbackConfirm");
		feedbackConfirm.setOutputMarkupId(true);
		feedbackConfirm.setOutputMarkupPlaceholderTag(true);
		add(feedbackConfirm);

		// =====================================================
		// form and form fields components
		// =====================================================
		final SearchParameterBean searchParameterBean = new SearchParameterBean();
		final SearchForm<SearchParameterBean> searchForm = new SearchForm<SearchParameterBean>("searchForm", searchParameterBean,
				dataContainer);

		add(searchForm);

		// =====================================================
		// model for the result list and load data
		// =====================================================

		Pageable pageInfo = new PageRequest(currentPage, LINES_PER_PAGE);

		final MarketListModel marketListModel = new MarketListModel(pageInfo, searchParameterBean);
		marketListModel.getObject();

		// =====================================================
		// Modal page for updating contract
		// =====================================================

		final OPopinContainer modalPopIn = new OPopinContainer("modalMarketWindow");

		dataContainer.add(modalPopIn);

		final OPopinContainer modalUpdatePopIn = new OPopinContainer("modalUpdateMarketWindow");
		dataContainer.add(modalUpdatePopIn);
		final UpdateMarketPanel updateMarketPanel = new UpdateMarketPanel("panelUpdate", new MarketBean(), modalUpdatePopIn);
		modalUpdatePopIn.add(updateMarketPanel);

		MarketDataProvider dp = new MarketDataProvider(searchParameterBean);

		final MarketListView resultListView = new MarketListView("result", dp, LINES_PER_PAGE, modalUpdatePopIn, updateMarketPanel,
				dataContainer);

		// =====================================================
		// style table
		// =====================================================

		dataContainer.add(resultListView);
		dataContainer.add(new OrderByBorder<String>("orderByName", "name", dp));
		dataContainer.add(new OrderByBorder<String>("orderById", "id", dp));

		OAjaxPagingNavigator pagingNavigator = new OAjaxPagingNavigator("navigator", (IPageable) resultListView);
		dataContainer.add(feedbackConfirm);

		// =====================================================
		// paging navigator for contracts list
		// =====================================================
		dataContainer.add(pagingNavigator);

		// =====================================================
		// counter with a dynamic model
		// =====================================================

		final Label counterLabel = new Label("resultCounter", new CounterModel<Long>(sizeModel));
		dataContainer.add(counterLabel);

		// =====================================================
		// panel Create Market
		// =====================================================

		final CreateMarketPanel createMarketPanel = new CreateMarketPanel("panelCreate", modalPopIn);
		createMarketPanel.setOutputMarkupId(true);
		modalPopIn.add(createMarketPanel);

		// =====================================================
		// Button to open form for opening form to create market
		// =====================================================

		OPopinAjaxOpenLink<MarketBean> openModalCreate = new OPopinAjaxOpenLink<MarketBean>("createMarketButton", modalPopIn) {
			private static final long serialVersionUID = 1L;

			@Override
			public void onClick(AjaxRequestTarget target) {
				createMarketPanel.setMarketBean(new MarketBean());
				target.add(modalPopIn);
				target.add(createMarketPanel);
				super.onClick(target);
			}
		};
		add(openModalCreate);

	}

	/**
	 * private class representing the search form
	 * 
	 * @author APT
	 * 
	 * @param <T>
	 *            The bean filled by the search form
	 */
	private class SearchForm<T> extends Form<T> {

		private static final long serialVersionUID = 1L;
		private final Component container;

		public SearchForm(String id, T searchBean, Component container) {
			super(id, new CompoundPropertyModel<T>(searchBean));

			// CoumpoundPropertyModel on SearchParameterBean
			add(new TextField<String>("marketName"));
			this.container = container;
			add(new OButtonStrong("searchMarket", new ResourceModel("button.search")));
		}

		@Override
		protected void onSubmit() {
			// data is retrieved into model only if the component is visible
			formSubmitted = true;
			container.setVisible(true);

			if (LOG.isDebugEnabled()) {
				T searchParam = getModelObject();
				LOG.debug("Search parameters = " + searchParam.toString());
			}
		}
	}

	/**
	 * private class representing the model containing the result list of markets found
	 * 
	 * @author APT
	 * 
	 */
	private class MarketListModel extends LoadableDetachableModel<List<MarketBean>> {
		private static final long serialVersionUID = 1L;
		private final Pageable pageInfo;
		private final SearchParameterBean searchParameterBean;

		public MarketListModel(Pageable pageInfo, SearchParameterBean searchParameterBean) {
			this.searchParameterBean = searchParameterBean;
			this.pageInfo = pageInfo;
		}

		@Override
		protected List<MarketBean> load() {
			List<MarketBean> resultListBean = null;
			Page<MarketBean> pageResult = null;
			// get data from application layer
			try {

				LOG.debug("loading data for market names like {}", searchParameterBean.getMarketName());

				pageResult = manageMarket.findMarketByName(pageInfo, searchParameterBean.getMarketName());
				resultListBean = pageResult.getContent();

			} catch (NotFoundException e) {
				LOG.debug("market {} not found", searchParameterBean.getMarketName(), e);
				resultListBean = new ArrayList<MarketBean>();
				if (formSubmitted) {
					error(getString("warning.not.found"));
				}
			} catch (TechnicalException e) {
				LOG.error("a technical exception has occured", e);
				throw new RestartResponseException(getApplication().getApplicationSettings().getInternalErrorPage());
			}
			formSubmitted = false;
			return resultListBean;
		}
	}

	/**
	 * private class representing the pageable list view of markets
	 * 
	 * @author APT
	 * 
	 */

	private class MarketListView extends DataView<MarketBean> {
		private static final long serialVersionUID = 1L;

		private OPopinContainer modalUpdatePopIn;
		private UpdateMarketPanel updateMarketPanel;

		private WebMarkupContainer dataContainer;

		public MarketListView(String id, MarketDataProvider model, int rowsPerPage, OPopinContainer modalUpdatePopIn,
				UpdateMarketPanel updateMarketPanel, WebMarkupContainer dataContainer) {
			super(id, model, rowsPerPage);
			this.modalUpdatePopIn = modalUpdatePopIn;
			this.updateMarketPanel = updateMarketPanel;
			this.dataContainer = dataContainer;

		}

		// ==========================
		// create modify market event
		// ==========================

		@Override
		public void onEvent(IEvent<?> event) {
			super.onEvent(event);

			if (event.getPayload() instanceof CreateOrUpdateMarketEvent) {
				AjaxRequestTarget target = ((CreateOrUpdateMarketEvent) event.getPayload()).getAjaxRequestTarget();

				// refresh list
				target.add(dataContainer);
			}
		}

		@Override
		public void populateItem(final Item<MarketBean> item) {
			final MarketBean marketBean = item.getModelObject();

			LOG.debug("populate market #{} : {}", marketBean.getId(), marketBean.toString());

			// Use CompoundPropertyModel on MarketBean
			item.add(new Label("id"));

			OPopinAjaxOpenLink<MarketBean> openModalupdateMarketLink = new OPopinAjaxOpenLink<MarketBean>("lmarketId",
					modalUpdatePopIn) {
				private static final long serialVersionUID = 1L;

				@Override
				public void onClick(AjaxRequestTarget target) {

					LOG.debug("updating market, parameters = {}", marketBean.toString());
					target.add(modalUpdatePopIn);
					updateMarketPanel.setMarketBean(marketBean);
					updateMarketPanel.setOutputMarkupId(true);
					target.add();
					super.onClick(target);
				}
			};

			openModalupdateMarketLink.add(new Label("name"));
			item.add(openModalupdateMarketLink);
			item.add(new Label("description"));
			DeleteLink d = new DeleteLink("deleteLink", marketBean);
			d.add(new Image("btnDelete", new PackageResourceReference("btnDelete.gif")));
			item.add(d);

			item.add(AttributeModifier.replace("class", new AbstractReadOnlyModel<String>() {
				private static final long serialVersionUID = 1L;

				@Override
				public String getObject() {
					return (item.getIndex() % 2 == 1) ? "even" : "odd";
				}
			}));

		}

	}

	private class MarketDataProvider extends SortableDataProvider<MarketBean, String> { // implements
																						// ISortStateLocator<String>{

		private SearchParameterBean searchParameterBean;
		private static final long serialVersionUID = 1L;

		public MarketDataProvider(final SearchParameterBean searchParameterBean) {
			this.searchParameterBean = searchParameterBean;
			// no sort default;
		}

		@Override
		public Iterator<? extends MarketBean> iterator(long first, long count) {

			Pageable pageInfo = null;
			int firstToCalculate = ((Long) first).intValue();
			int firstPage = firstToCalculate / LINES_PER_PAGE;
			Direction dir = null;

			// when click on OrderByBorder

			SortParam<String> test = getSort();
			setSort(test);
			if (getSort() == null || getSort().getProperty() == null || "".equals(getSort().getProperty())) {
				pageInfo = new PageRequest(firstPage, LINES_PER_PAGE);
			} else {
				if (getSort().isAscending()) {
					dir = Direction.ASC;
				} else {
					dir = Direction.DESC;
				}
				sort = new Sort(dir, getSort().getProperty());
				pageInfo = new PageRequest(firstPage, LINES_PER_PAGE, sort);
			}

			return new MarketListModel(pageInfo, searchParameterBean).getObject().iterator();
		}

		@Override
		public long size() {
			try {
				size = manageMarket.countMarketByName(searchParameterBean.getMarketName());
			} catch (NotFoundException e) {
				LOG.debug("market {} not found", searchParameterBean.getMarketName(), e);
				error(getString("warning.not.found"));
			} catch (TechnicalException e) {
				LOG.error("a technical exception has occured", e);
				throw new RestartResponseException(getApplication().getApplicationSettings().getInternalErrorPage());
			}

			sizeModel.setObject(size);
			return size;
		}

		/**
		 * wrap items in a CompoundPropertyModel with a detacable model
		 */

		@Override
		public IModel<MarketBean> model(final MarketBean object) {
			return new CompoundPropertyModel<MarketBean>(new LoadableDetachableModel<MarketBean>((MarketBean) object) {

				private static final long serialVersionUID = 1L;

				@Override
				protected MarketBean load() {
					return object;
				}
			});
		}

	}

	/**
	 * link for delete market
	 * 
	 */
	private class DeleteLink extends Link<Void> {
		private static final long serialVersionUID = 1L;
		private final MarketBean marketBean;

		public DeleteLink(String id, MarketBean marketBean) {
			super(id);
			this.marketBean = marketBean;

			String message = new StringResourceModel("deleteMarketPage.confirm", new Model<Object[]>(
					new Object[] { marketBean.getName() })).getString();
			add(new AttributeModifier("onclick", "return confirm('" + message.replace("'", "\\'") + "');"));
		}

		@Override
		public void onClick() {
			LOG.debug("deleting market #" + marketBean.getId());

			try {
				manageMarket.deleteMarket(marketBean.getId());
				// feedbackConfirm not store in session with wicket 6
				feedbackConfirm
						.success(getString("info.marketDeleted", new Model<Object[]>(new Object[] { marketBean.getName() })));
			} catch (DataAccessException e) {
				LOG.debug("market {} does not exist", marketBean.getName(), e);
				warn(getString("warning.marketDoesNotExist", new Model<Object[]>(new Object[] { marketBean.getName() })));
			} catch (InvalidMarketException e) {
				LOG.error("Should not occur : no constraint on market deletion", e);
				throw new RestartResponseException(getApplication().getApplicationSettings().getInternalErrorPage());
			} catch (TechnicalException e) {
				LOG.error("a technical exception has occured", e);
				throw new RestartResponseException(getApplication().getApplicationSettings().getInternalErrorPage());
			}
		}

	}

}